package com.bfsd.tieba.agent.tieba_agent.tools;

import com.alibaba.fastjson2.JSONObject;
import com.bfsd.tieba.agent.tieba_agent.config.TiebaConfig;
import com.bfsd.tieba.agent.tieba_agent.enums.Consts;
import com.bfsd.tieba.agent.tieba_agent.model.vo.PostBriefVO;
import com.bfsd.tieba.agent.tieba_agent.model.vo.PostVO;
import com.bfsd.tieba.agent.tieba_agent.model.vo.ReplyVO;
import com.bfsd.tieba.agent.tieba_agent.model.vo.TiebaResultVO;
import com.bfsd.tieba.agent.tieba_agent.model.entity.post.FirstPostContent;
import com.bfsd.tieba.agent.tieba_agent.model.entity.post.Post;
import com.bfsd.tieba.agent.tieba_agent.model.entity.TiebaResult;
import com.bfsd.tieba.agent.tieba_agent.model.entity.TiebaUser;
import com.bfsd.tieba.agent.tieba_agent.model.entity.reply.Reply;
import com.bfsd.tieba.agent.tieba_agent.utils.RequestUtil;
import io.micrometer.common.util.StringUtils;
import jakarta.annotation.Resource;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

@Component
public class TieBaTools {
    @Resource
    private TiebaConfig config;
    @Resource
    RequestUtil requestUtil;

    private static final ThreadLocal<String> planFilePath = new ThreadLocal<>();
    private static final ThreadLocal<StringBuffer> seek = new ThreadLocal<>();

    /**
     * 帖子和帖子id的映射map
     */
    private final Map<String, PostBriefVO> postTitleIdMap = new HashMap<>();
    /**
     * 用户ID和名称的映射map
     */
    private final Map<Long, String> userIdNameMap = new HashMap<>();

    @Tool(description = "时间工具,用来获取当前时间")
    public String watch() {
        Date date = new Date(System.currentTimeMillis());
        return Consts.sdf.format(date);
    }


    @Tool(description = "等待一会,用来等待的工具")
    public String waitAMinute(@ToolParam(description = "我要等几分钟") Integer minute) throws InterruptedException {
        //作为等待新帖来说，分钟单位甚至过短了,如果测试功能，可以改成秒
        long time = minute * 60000L;
        for (long i = time; i > 0; i = i - 1000) {
            Thread.sleep(1000);
            float seconds = (time - i) / 1000f;
            System.out.printf("\r小八等待了" + seconds + "秒");
        }
        System.out.println();
        seek.get().append("小八休息了").append(minute).append("分钟\n");
        return "小八休息了" + minute + "分钟";
    }


    @Tool(description = "完成步骤,获取下一个步骤")
    public String finishStep(
            @ToolParam(description = "我完成了步骤几") Integer step,
            @ToolParam(description = "我要获取步骤几，完成所有任务传0") Integer nextStep
            , @ToolParam(description = "我是否完成任务了,0=否，1=是") Integer isFinishTask) throws IOException {
        String fileName = planFilePath.get();
        if (fileName == null) {
            throw new IllegalStateException("没有找到执行计划");
        }
        Path filePath = Paths.get("src", "main", "resources", "plans", fileName);
        // 读取文件所有行
        List<String> lines = Files.readAllLines(filePath);
        // 查找以itemNumber开头的行
        Optional<String> lineOptional = lines.stream()
                .filter(line -> line.startsWith(String.valueOf(step)))
                .findFirst();
        if (lineOptional.isEmpty()) {
            throw new IllegalArgumentException("没有找到步骤: " + step);
        }
        String line = lineOptional.get();
        int lineIndex = lines.indexOf(line);
        // 处理行内容，添加打钩标记
        String processedLine;
        if (line.contains("[ ]")) {
            // 情况1：未打钩 → 打钩并添加"完成1次"
            processedLine = line.replace("[ ]", "[✓]") + " 完成1次";
        } else if (line.contains("[✓]")) {
            // 情况2：已打钩 → 查找并更新完成次数
            processedLine = updateCompletionCount(line);
        } else {
            processedLine = line;
        }
        // 更新行内容
        lines.set(lineIndex, processedLine);
        // 获取下一个步骤的内容
        StringBuilder nextStepContent = new StringBuilder();
        if (isFinishTask == 1) {
            //完成任务删除，防止内存泄漏
            lines.add(""); // 空行分隔
            lines.add(seek.get().toString());
            planFilePath.remove();
            seek.remove();
        }else{
            boolean collecting = false;
            for (int i = 0; i < lines.size(); i++) {
                String currentLine = lines.get(i).trim();
                // 检查是否找到下一个步骤的开始
                if (currentLine.startsWith(nextStep + ".")) {
                    collecting = true;
                    nextStepContent.append(lines.get(i)).append("\n");
                    continue;
                }
                // 如果已经开始收集，检查是否是下一个步骤的开始
                if (collecting) {
                    if (i + 1 < lines.size()) {
                        String nextLine = lines.get(i + 1).trim();
                        // 如果下一行是新的步骤开头，则停止收集
                        if (nextLine.matches("^\\d+\\..*")) {
                            nextStepContent.append(lines.get(i)).append("\n");
                            break;
                        }
                    }
                    nextStepContent.append(lines.get(i)).append("\n");
                }
            }

        }
        // 写回文件
        Files.write(filePath, lines);
        System.out.println("小八完成了步骤" + processedLine);
        return isFinishTask == 1 ? "已完成所有步骤" : nextStepContent.toString();
    }


    @Tool(description = "写下我的执行计划")
    public String writePlan(@ToolParam(description = "我想写什么") String plan,
                            @ToolParam(description = "这个计划的名称,提取用户语义的关键需求起一个名字") String planName) throws IOException {
        // 确保resources/plans目录存在
        Path plansDir = Paths.get("src", "main", "resources", "plans");
        if (!Files.exists(plansDir)) {
            Files.createDirectories(plansDir);
        }
        // 生成带时间戳的文件名
        String fileName = System.currentTimeMillis() + "_" + planName + "_执行计划.txt";
        Path filePath = plansDir.resolve(fileName);
        // 写入文件内容
        Files.writeString(filePath, plan, StandardOpenOption.CREATE);
        planFilePath.set(fileName);
        seek.set(new StringBuffer().append("\n思考过程:\n"));
        System.out.println("小八在resources/plans/下写下了他的执行计划：");
        System.out.println(plan);
        return "写入计划成功";
    }

    @Tool(description = "只要我和用户分享想法，就用这个工具说出来，完成步骤后请先使用finishStep工具再使用这个工具")
    public void printSeekMsg(@ToolParam(description = "我想说什么") String seekMsg) {
        System.out.println(seekMsg);
        seek.get().append(seekMsg).append("\n");
    }

    @Tool(description = "用来逛贴吧，看看吧里都发了啥")
    public String fetchPosts(
            @ToolParam(description = "我要逛哪个吧，不要带吧字，比如java吧，填java") String barName
            , @ToolParam(description = "我想看第几页的帖子，从1开始算") int page
            , @ToolParam(description = "帖子要怎么排，0=默认(看热门)，1=按发帖时间最新") int sortType) {
        System.out.println("「小八打开了" + barName + "吧的第" + page + "页,点击了" + (sortType == 0 ? "默认" : "按发帖时间") + "排序」");
        Map<String, String> params = new HashMap<>();
        params.put(Consts.SORT_TYPE_KEY, "" + sortType);
        params.put(Consts.BAR_NAME_KEY, barName);
        params.put(Consts.PAGE_NUMBER_KEY, String.valueOf((page - 1) * 50));
        //动态请求，封装通用请求
        TiebaResult tiebaResult = requestUtil.executeRequestByUrlAndParam(params, config.getUrls().getPostUrl(), TiebaResult.class);
        tiebaResult.setBarName(barName);
        List<PostVO> postVOS = convertTiebaResultToResultBO(tiebaResult).getPostVOS();
        return JSONObject.toJSONString(postVOS);
    }

    @Tool(description = "点进帖子里去看看，看看帖子都有啥回复")
    public String fetchReplies(@ToolParam(description = "我要看哪个帖子的回复,填帖子名就好了") String postName
            , @ToolParam(description = "我想看第几页的回复，从1开始算") int page) {
        System.out.println("「小八点开了" + postName + "的第" + page + "页」");
        //是否只看楼主这个字段，发现这个让ai填效果不好，ai不知道啥时候该选哪个，所以改成手动调两次，一次只看楼主，然后获取楼主名称，再做一次只看网友，组装起来返回
        List<ReplyVO> replyVOS = getReplyVOS(postName, page);
        if (replyVOS == null) return "没有找到这个帖子";
        return JSONObject.toJSONString(replyVOS);
    }


    @Tool(description = "用来给帖子留下评论，发表我的看法")
    public String replyToPost(@ToolParam(description = "我要回复哪个帖子,填帖子名就好了") String postName
            , @ToolParam(description = "我要回复什么内容") String content) throws InterruptedException {
        //ai评论太快会导致频繁，睡会
        Thread.sleep(1500);
        Map<String, String> params = new HashMap<>();
        //先通过帖子名查找，没找到就降级 找包含帖子名的key，因为有时候ai不会记住帖子里带的符号，颜文字等等
        PostBriefVO postBriefVO = null;
        if (!Objects.equals(null, postTitleIdMap.get(postName).getPostId())) {
            postBriefVO = postTitleIdMap.get(postName);
        } else {
            for (String s : postTitleIdMap.keySet()) {
                if (s.contains(postName)) {
                    postBriefVO = postTitleIdMap.get(s);
                }
            }
        }
        if (Objects.equals(null, postBriefVO)) {
            return "没有找到对应帖子，去吧里重新翻翻吧";
        }
        params.put(Consts.CONTENT_KEY, content);
        params.put(Consts.BDUSS_KEY, requestUtil.extractCookieValue(Consts.BDUSS_KEY));
        params.put(Consts.BAIDU_ID_KEY, requestUtil.extractCookieValue(Consts.BAIDU_ID_KEY));
        params.put(Consts.S_TOKEN_KEY, requestUtil.extractCookieValue(Consts.S_TOKEN_UPPER_CASE_KEY));
        params.put(Consts.BAR_ID_KEY, "" + postBriefVO.getBarId());
        params.put(Consts.POST_REPLY_ID_KEY, "" + postBriefVO.getPostId());
        params.put(Consts.BAR_NAME_KEY, postBriefVO.getBarName());
        params.put(Consts.TBS_KEY, requestUtil.getTBS());
        //动态请求，封装通用请求
        JSONObject tbResult = requestUtil.executeRequestByUrlAndParam(params, config.getUrls().getReplyPostUrl(), JSONObject.class);
        System.out.println("小八点开了" + postName + ",评论了一句：\n" + content);
        return StringUtils.isNotBlank(tbResult.getString(Consts.ERROR_MSG_KEY)) ?
                tbResult.getString(Consts.ERROR_MSG_KEY) : tbResult.getString(Consts.MSG_KEY);
    }


    @Tool(description = "用来发帖，发表我的看法或者寻求帮助，询问等")
    public String post(
            @ToolParam(description = "帖子标题") String title,
            @ToolParam(description = "帖子内容") String content
            , @ToolParam(description = "发在哪个吧，不要带吧字，比如java吧，填java") String barName) {
        Map<String, String> params = new HashMap<>();
        params.put(Consts.TBS_KEY, requestUtil.getTBS());
        String fid = requestUtil.getFid(barName);
        params.put(Consts.BAR_ID_KEY, fid);
        params.put(Consts.TITLE_KEY, title);
        params.put(Consts.CONTENT_KEY, content);
        params.put(Consts.BAR_NAME_KEY, barName);
        JSONObject tiebaResult = requestUtil.executeRequestByUrlAndParam(params, config.getUrls().getThreadAddUrl(), JSONObject.class);
        if (Objects.equals(null, tiebaResult.getString(Consts.MSG_KEY))) {
            //没返回msg说明发帖失败
            return tiebaResult.getString(Consts.ERROR_MSG_KEY);
        }
        //发完帖缓存，否则自己评论自己发的贴会找不到
        PostBriefVO postBriefVO = new PostBriefVO();
        postBriefVO.setBarId(Long.valueOf(fid));
        postBriefVO.setPostId(tiebaResult.getLong(Consts.POST_ID));
        postBriefVO.setBarName(barName);
        postTitleIdMap.put(title, postBriefVO);
        System.out.println("小八在" + barName + "吧发了一个帖子：" + title);
        System.out.println("内容：" + content);
        System.out.println("内容：" + content);
        return "发帖成功";
    }

    /**
     * ---------------------------------私有方法-----------------------------------
     */
    private List<ReplyVO> getReplyVOS(String postName, int page) {
        Map<String, String> params = new HashMap<>();
        //先通过帖子名查找，没找到就降级 找包含帖子名的key，因为有时候ai不会记住帖子里带的符号，颜文字等等
        //通过内存获取帖子ID
        if (!Objects.equals(null, postTitleIdMap.get(postName))) {
            params.put(Consts.POST_ID_KEY, "" + postTitleIdMap.get(postName).getPostId());
        } else {
            for (String s : postTitleIdMap.keySet()) {
                if (s.contains(postName)) {
                    params.put(Consts.POST_ID_KEY, "" + postTitleIdMap.get(s).getPostId());
                }
            }
        }
        //幻觉帖子，可能是用户提出错误帖子，或者ai根本没去翻吧
        if (params.get(Consts.POST_ID_KEY) == null) {
            return null;
        }
        params.put(Consts.ONLY_LZ_KEY, Consts.NOT_ONLY_LZ);
        //是否带楼中楼回复
        params.put(Consts.WITH_FLOOR_KEY, Consts.YES);
        //带回复的数量 先定个五条
        params.put(Consts.FLOOR_RN_KEY, "5");
        params.put(Consts.PAGE_NUMBER_KEY, "" + page);
        //动态请求，封装通用请求
        TiebaResult tiebaResult = requestUtil.executeRequestByUrlAndParam(params, config.getUrls().getReplyUrl(), TiebaResult.class);
        //这步是把贴吧响应转为对AI来说可读性较高的bo，去除了无用字段，内部逻辑别管了，直接看BO吧
        return convertTiebaResultToResultBO(tiebaResult).getReplyVOS();
    }

    /**
     * 通用方法，把贴吧返回的结果转换为自己定义的BO
     *
     */
    private TiebaResultVO convertTiebaResultToResultBO(TiebaResult tiebaResult) {
        TiebaResultVO tiebaResultVO = new TiebaResultVO();
        List<PostVO> postVOS = tiebaResultVO.getPostVOS();
        List<ReplyVO> replyVOS = tiebaResultVO.getReplyVOS();
        for (TiebaUser tiebaUser : tiebaResult.getUser_list()) {
            userIdNameMap.put(tiebaUser.getId(), tiebaUser.getName());
        }
        // 格式化为字符串
        if (Objects.nonNull(tiebaResult.getThread_list()) && !tiebaResult.getThread_list().isEmpty()) {
            for (Post post : tiebaResult.getThread_list()) {
                if (post.getIs_top().equals(1)) {
                    continue;
                }

                Date date = new Date(post.getCreate_time() * 1000L);
                PostVO postVO = new PostVO();
                postVO.setPostTime(Consts.sdf.format(date));
                postVO.setReplyCount(post.getReply_num());
                postVO.setTitle(post.getTitle());
                postVO.setUserName(userIdNameMap.get(post.getAuthor_id()));
                postVO.setContent("");
                if (!Objects.equals(null, post.getFirst_post_content()) && !post.getFirst_post_content().isEmpty()) {
                    if ((post.getFirst_post_content().get(0).getType()).equals(Consts.TEXT))
                        postVO.setContent(post.getFirst_post_content().get(0).getText());
                }
                //标题与id映射，方便后续AI查询回复
                PostBriefVO postBriefVO = new PostBriefVO();
                postBriefVO.setPostId(post.getTid());
                postBriefVO.setBarId(post.getFid());
                postBriefVO.setBarName(tiebaResult.getBarName());
                postTitleIdMap.put(postVO.getTitle(), postBriefVO);
                postVOS.add(postVO);
            }
        }
        if (Objects.nonNull(tiebaResult.getPost_list()) && !tiebaResult.getPost_list().isEmpty()) {
            for (Reply reply : tiebaResult.getPost_list()) {
                if (isFirstFloor(reply)) continue;
                //构建主回复
                ReplyVO mainReply = buildMainReply(reply);
                //构建楼中楼
                processSubReplies(reply, mainReply, userIdNameMap);
                replyVOS.add(mainReply);
            }
        }

        return tiebaResultVO;
    }

    /**
     * 辅助打钩方法，可以处理重复请求
     *
     */
    private String updateCompletionCount(String line) {
        // 匹配 "完成X次" 的正则表达式
        Pattern pattern = Pattern.compile("完成(\\d+)次");
        Matcher matcher = pattern.matcher(line);

        if (matcher.find()) {
            // 如果找到"完成X次"，数字+1
            int count = Integer.parseInt(matcher.group(1)) + 1;
            return line.replaceAll("完成\\d+次", "完成" + count + "次");
        } else {
            // 如果没找到，直接追加"完成1次"
            return line + " 完成1次";
        }
    }

    // 判断是否一楼
    private boolean isFirstFloor(Reply reply) {
        return reply.getFloor() == 1;
    }

    // 构建主回复
    private ReplyVO buildMainReply(Reply reply) {
        ReplyVO bo = buildBaseReply(reply, userIdNameMap);
        bo.setSubReplyCount(reply.getSub_post_number());
        bo.setSubReplyList(new ArrayList<>());
        return bo;
    }

    // 构建基础回复（主回复和子回复共用）
    private ReplyVO buildBaseReply(Reply reply, Map<Long, String> userIdNameMap) {
        ReplyVO bo = new ReplyVO();
        bo.setReplyTime(formatUnixTime(reply.getTime()));
        bo.setFloor(reply.getFloor());
        bo.setContent(extractTextContent(reply.getContent()));
        bo.setTitle(reply.getTitle());
        bo.setUserName(getUserNameSafe(userIdNameMap, reply.getAuthor_id()));
        return bo;
    }

    // 处理子回复
    private void processSubReplies(Reply mainReply, ReplyVO mainReplyVO,
                                   Map<Long, String> userIdNameMap) {
        List<Reply> subReplies = getSubRepliesSafe(mainReply);
        subReplies.forEach(subReply -> {
            ReplyVO subBO = buildSubReply(subReply, userIdNameMap);
            mainReplyVO.getSubReplyList().add(subBO);
        });
    }

    // 构建子回复
    private ReplyVO buildSubReply(Reply subReply, Map<Long, String> userIdNameMap) {
        ReplyVO bo = buildBaseReply(subReply, userIdNameMap);
        bo.setSubReplyCount(subReply.getSub_post_number());
        return bo;
    }

    //============= 工具方法 ==============
// 安全获取用户名
    private String getUserNameSafe(Map<Long, String> userMap, Long userId) {
        return userMap.getOrDefault(userId, "未知用户");
    }

    // 安全获取子回复列表
    private List<Reply> getSubRepliesSafe(Reply reply) {
        if (reply.getSub_post_list() == null) return Collections.emptyList();
        return reply.getSub_post_list().getSub_post_list() != null ?
                reply.getSub_post_list().getSub_post_list() :
                Collections.emptyList();
    }

    // 时间格式化
    private String formatUnixTime(long unixTime) {
        return Consts.sdf.format(new Date(unixTime * 1000L));
    }

    // 提取文本内容（假设Content类的结构）
    private String extractTextContent(List<FirstPostContent> contents) {
        if (CollectionUtils.isEmpty(contents)) return "";

        FirstPostContent firstContent = contents.get(0);
        return (firstContent != null &&
                String.valueOf(firstContent.getType()).equals(Consts.TEXT)) ?
                firstContent.getText() : "";
    }

}
